/*
 * 上海远境文化传媒有限公司 版权所有
 */
package helper;

import com.github.stuxuhai.jpinyin.PinyinFormat;
import com.github.stuxuhai.jpinyin.PinyinHelper;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import play.Logger;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.function.Function;

/**
 * 文本Helper.
 */
public class TextHelper {

    /**
     * utf8是变长字符集，单个字符占用1～4个字节
     * mysql在选择utf8字符集时，最多只能存储3个字节的utf8字符，如果想要保存任意的utf8字符，数据必须用utf8mb4字符集
     * 有些情况下，不能变更已选定的字符集，只好不得以而为之，把输入中的4个字节的utf8字符全部过滤掉
     * 好在，utf8字符集中，汉字是3个字节的
     */
    public static String filterOffUtf8Mb4(String text) {
        if (StringUtils.isBlank(text)) {
            return text;
        }
        byte[] bytes = text.getBytes(Charset.forName("UTF-8"));
        ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
        int i = 0;
        while (i < bytes.length) {
            short b = bytes[i];
            if (b > 0) {
                buffer.put(bytes[i++]);
                continue;
            }
            b += 256;
            if ((b ^ 0xC0) >> 4 == 0) {
                buffer.put(bytes, i, 2);
                i += 2;
            } else if ((b ^ 0xE0) >> 4 == 0) {
                buffer.put(bytes, i, 3);
                i += 3;
            } else if ((b ^ 0xF0) >> 4 == 0) {
                i += 4;
            }
        }
        buffer.flip();
        return new String(buffer.array(), Charset.forName("UTF-8"));
    }

    /**
     * 完全匹配.
     * @param term 姓名  付世渠
     * @param query 查询词 fushiqu
     */
    public static String fullMatch(final CharSequence term, final CharSequence query) {
        return fullMatch(term, query, m -> "<em>" + m + "</em>");
    }
    private static String fullMatch(final CharSequence term, final CharSequence query, Function<CharSequence, String> mapper) {
        Locale locale = Locale.CHINA;
        if (term == null || query == null) {
            throw new IllegalArgumentException("Strings must not be null");
        } else if (locale == null) {
            throw new IllegalArgumentException("Locale must not be null");
        }

        // fuzzy logic is case insensitive. We normalize the Strings to lower
        // case right from the start. Turning characters to lower case
        // via Character.toLowerCase(char) is unfortunately insufficient
        // as it does not accept a locale.
        final String termLowerCase = term.toString().toLowerCase(locale);
        final String queryLowerCase = query.toString().toLowerCase(locale);

        StringBuilder termBuilder = new StringBuilder(termLowerCase.length());

        String hanziPinyin = PinyinHelper.convertToPinyinString(termLowerCase, "", PinyinFormat.WITHOUT_TONE);

        //Logger.info("LOG298101: termLowerCase=%s,queryLowerCase=%s, hanziPinyin=%s", termLowerCase, queryLowerCase, hanziPinyin);

        if (queryLowerCase.equals(hanziPinyin)) {  //拼音完全匹配
            termBuilder.append(mapper.apply(term));
            return termBuilder.toString();
        }

        return null;
    }

    /**
     * fuzzy匹配.
     * @param term 姓名  付世渠
     * @param query 查询词 fushiqu
     */
    public static String extraFuzzyMatch(final CharSequence term, final CharSequence query) {
        return fuzzyMatch(term, query, m -> "<em>" + m + "</em>", true);
    }

    public static String fuzzyMatch(final CharSequence term, final CharSequence query) {
        return fuzzyMatch(term, query, m -> "<em>" + m + "</em>", false);
    }

    public static String fuzzyMatch(final CharSequence term, final CharSequence query, Function<Character, String> mapper, boolean extraMatch) {
        Locale locale = Locale.CHINA;
        if (term == null || query == null) {
            throw new IllegalArgumentException("Strings must not be null");
        } else if (locale == null) {
            throw new IllegalArgumentException("Locale must not be null");
        }

        // fuzzy logic is case insensitive. We normalize the Strings to lower
        // case right from the start. Turning characters to lower case
        // via Character.toLowerCase(char) is unfortunately insufficient
        // as it does not accept a locale.
        final String termLowerCase = term.toString().toLowerCase(locale);
        final String queryLowerCase = query.toString().toLowerCase(locale);

        // the resulting score
        int score = 0;

        // the position in the term which will be scanned next for potential
        // query character matches
        int termIndex = 0;

        // index of the previously matched character in the term
        int previousMatchingCharacterIndex = Integer.MIN_VALUE;

        StringBuilder termBuilder = new StringBuilder(termLowerCase.length());

        for (int queryIndex = 0; queryIndex < queryLowerCase.length(); queryIndex++) {
            final char queryChar = queryLowerCase.charAt(queryIndex);

            boolean termCharacterMatchFound = false;

            for (; termIndex < termLowerCase.length() && !termCharacterMatchFound; termIndex++) {
                final char termChar = termLowerCase.charAt(termIndex);
                final String termPyString = PinyinHelper.getShortPinyin(String.valueOf(termChar));

                Logger.debug("LOG02760: queryChar=%s, termChar=%s, termPyChar=%s, check=%s", queryChar, termChar, termPyString, termPyString.equals(String.valueOf(queryChar)));
                if (queryChar == termChar || termPyString.equals(String.valueOf(queryChar))) {
                    termBuilder.append(mapper.apply(term.charAt(termIndex)));
                    // simple character matches result in one point
                    score++;

                    // subsequent character matches further improve
                    // the score.
                    /*
                    if (previousMatchingCharacterIndex + 1 == termIndex && !extraMatch) {
                        score += 2;
                    }
                    */

                    previousMatchingCharacterIndex = termIndex;

                    // we can leave the nested loop. Every character in the
                    // query can match at most one character in the term.
                    termCharacterMatchFound = true;
                } else {
                    termBuilder.append(term.charAt(termIndex));
                }
            }
        }

        Logger.debug("LIE00100: score=%s, query.length=%s, term=%s(%s), query=%s",
                score, query.length(), term, term.length(), query);
        if (extraMatch) {
            if (score < query.length() || query.length() < term.length()) {
                return null;
            }
        } else {
            if (score < query.length()) {
                return null;
            }
        }
        termBuilder.append(term.subSequence(termIndex, term.length()));
        return termBuilder.toString();
    }

    public static String encryptPassword(String inputPassword, String passwordSalt) {

        String level1Password = DigestUtils.sha1Hex("0DF9798521E24FCDA45DDF44152B1FDC@" + inputPassword + "@" + passwordSalt);
        String level2Password = DigestUtils.sha1Hex("FD974D1A17944D5F8C1AC25BDFC6BE85@" + level1Password + "@" + passwordSalt);
        return DigestUtils.sha1Hex("34F25FB870244C81BACDD5142A6C2239@" + level2Password + "@" + passwordSalt);
    }

    public static boolean isValidPassword(String inputPassword, String encryptPassword, String passwordSalt) {
        if (StringUtils.isBlank(encryptPassword) || StringUtils.isBlank(passwordSalt) || StringUtils.isBlank(inputPassword)) {
            return false;
        }
        return encryptPassword.equals(encryptPassword(inputPassword, passwordSalt));
    }

    public static boolean isSimplePassword(String password) {
        return false;
    }


    static final String VERY_WEAK   = "VERY_WEAK";
    static final String WEAK        = "WEAK";
    static final String AVERAGE     = "AVERAGE";
    static final String STRONG      = "STRONG";
    static final String VERY_STRONG = "VERY_STRONG";
    static final String SECURE      = "SECURE";
    static final String VERY_SECURE = "VERY_SECURE";

    public static String getPwdSecurityLevel(String pPasswordStr) {
        String safelevel = VERY_WEAK;
        int grade = getPwdSecurityGradle(pPasswordStr);

        /*
         * 最后的评分标准:
         *   >= 90: 非常安全
         *   >= 80: 安全（Secure）
         *   >= 70: 非常强
         *   >= 60: 强（Strong）
         *   >= 50: 一般（Average）
         *   >= 25: 弱（Weak）
         *   >= 0: 非常弱
         */
        if (grade >= 90) {
            safelevel = VERY_SECURE;
        } else if (grade >= 80) {
            safelevel = SECURE;
        } else if (grade >= 70) {
            safelevel = VERY_STRONG;
        } else if (grade >= 60) {
            safelevel = STRONG;
        } else if (grade >= 50) {
            safelevel = AVERAGE;
        } else if (grade >= 25) {
            safelevel = WEAK;
        } else if (grade >= 0) {
            safelevel = VERY_WEAK;
        }
        return safelevel;
    }

    public static int getPwdSecurityGradle(String pPasswordStr) {
        if (pPasswordStr == null) {
            return 0;
        }
        int grade = 0;
        int index = 0;
        char[] pPsdChars = pPasswordStr.toCharArray();

        int numIndex = 0;
        int sLetterIndex = 0;
        int lLetterIndex = 0;
        int symbolIndex = 0;

        for (char pPsdChar : pPsdChars) {
            int ascll = pPsdChar;
            /*
             * 数字 48-57 A-Z 65 - 90 a-z 97 - 122 !"#$%&'()*+,-./ (ASCII码：33~47)
             * :;<=>?@ (ASCII码：58~64) [\]^_` (ASCII码：91~96) {|}~
             * (ASCII码：123~126)
             */
            if (ascll >= 48 && ascll <= 57) {
                numIndex++;
            } else if (ascll >= 65 && ascll <= 90) {
                lLetterIndex++;
            } else if (ascll >= 97 && ascll <= 122) {
                sLetterIndex++;
            } else if ((ascll >= 33 && ascll <= 47)
                    || (ascll >= 58 && ascll <= 64)
                    || (ascll >= 91 && ascll <= 96)
                    || (ascll >= 123 && ascll <= 126)) {
                symbolIndex++;
            }
        }
        /*
         * 一、密码长度: 5 分: 小于等于 4 个字符 10 分: 5 到 7 字符 25 分: 大于等于 8 个字符
         */
        if (pPsdChars.length <= 4) {
            index = 5;
        } else if (pPsdChars.length <= 7) {
            index = 10;
        } else {
            index = 25;
        }
        grade += index;

        /*
         * 二、字母: 0 分: 没有字母 10 分: 全都是小（大）写字母 20 分: 大小写混合字母
         */
        if (lLetterIndex == 0 && sLetterIndex == 0) {
            index = 0;
        } else if (lLetterIndex != 0 && sLetterIndex != 0) {
            index = 20;
        } else {
            index = 10;
        }
        grade += index;
        /*
         * 三、数字: 0 分: 没有数字 10 分: 1 个数字 20 分: 大于 1 个数字
         */
        if (numIndex == 0) {
            index = 0;
        } else if (numIndex == 1) {
            index = 10;
        } else {
            index = 20;
        }
        grade += index;

        /*
         * 四、符号: 0 分: 没有符号 10 分: 1 个符号 25 分: 大于 1 个符号
         */
        if (symbolIndex == 0) {
            index = 0;
        } else if (symbolIndex == 1) {
            index = 10;
        } else {
            index = 25;
        }
        grade += index;
        /*
         * 五、奖励: 2 分: 字母和数字 3 分: 字母、数字和符号 5 分: 大小写字母、数字和符号
         */
        if ((sLetterIndex != 0 || lLetterIndex != 0) && numIndex != 0) {
            index = 2;
        } else if ((sLetterIndex != 0 || lLetterIndex != 0) && numIndex != 0
                && symbolIndex != 0) {
            index = 3;
        } else if (sLetterIndex != 0 && lLetterIndex != 0 && numIndex != 0
                && symbolIndex != 0) {
            index = 5;
        }
        grade += index;
        return grade;
    }

    public static String getLoginFromEmail(String email) {
        if (StringUtils.isBlank(email)) {
            return null;
        }
        int location = email.indexOf("@");
        if (location > 0) {
            return email.substring(0, location);
        }
        return email;
    }
}
